package com.caishi.lkx.user.api;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.caishi.lkx.common.BaseApi;
import com.caishi.lkx.common.context.RequireContext;
import com.caishi.lkx.common.fliter.TokenFilter;
import com.caishi.lkx.common.ienum.DeviceType;
import com.caishi.lkx.common.role.Role;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.common.util.SmsUtilService;
import com.caishi.lkx.user.config.ThirdOauthConfig;
import com.caishi.lkx.user.dto.LoginDto;
import com.caishi.lkx.user.ienum.type.UserAccountType;
import com.caishi.lkx.user.ienum.type.UserType;
import com.caishi.lkx.user.model.*;
import com.caishi.lkx.user.service.*;
import com.caishi.lkx.user.service.impl.UserServiceImpl;
import com.zzw.common.SpringContextHolder;
import com.zzw.common.annotation.ApiTimeRecord;
import com.zzw.common.exception.BizException;
import com.zzw.common.lock.RedissonLock;
import com.zzw.common.service.TypeServiceRouter;
import com.zzw.common.utils.MD5Util;

import com.caishi.lkx.common.WeChatUtil;

import com.caishi.lkx.user.UserResultCode;

import com.caishi.lkx.user.login.LoginServiceComposite;
import com.caishi.lkx.user.login.WxcxLoginServiceImpl;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Locale;

/**
 * @author by keray
 * date:2019/8/1 10:07
 */
@Tag(name = "登录注册相关")
@RestController
@Slf4j
@RequestMapping("/user")
@Roles
public class LoginApi extends BaseApi {
    @Resource
    private PlatformTransactionManager transactionManager;


    @Resource
    private LoginServiceComposite loginServiceComposite;
    @Resource
    private TokenFilter tokenFilter;


    @Resource
    private ILoginInfoService loginInfoService;

    @Resource
    private SmsUtilService smsUtilService;

    @Resource
    private UserServiceImpl userService;

    @Resource
    private IUserDeviceService userDeviceService;

    @Resource
    private RedissonLock redissonLock;
    @Resource
    private IMemberService memberService;
    @Resource
    private ThirdOauthConfig thirdOauthConfig;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private IDistributionUserService IDistributionUserService;
    @Resource
    private IUserActiveService userActiveService;

    @Operation(summary = "登录接口")
    @Parameters({
            @Parameter(name = "deviceType", description = "设备类型", required = true),
            @Parameter(name = "deviceId", description = "设备id 移动端时必传"),
            @Parameter(name = "deviceName", description = "设备名 移动端时必传"),
            @Parameter(name = "userAccount", description = "账号或者手机号", required = true),
            @Parameter(name = "credential", description = "非快捷登录时必传密码"),
            @Parameter(name = "type", description = "登录类型", required = true),
            @Parameter(name = "code", description = "验证码登录时必传"),
            @Parameter(name = "token", description = "易盾token"),
            @Parameter(name = "accessToken", description = "运营商授权码"),
            @Parameter(name = "userType", description = "用户类型"),
    })
    @PostMapping("/login")
    @RequireContext(value = {com.caishi.lkx.common.context.Role.APP, com.caishi.lkx.common.context.Role.PC})
    public Object login(
            @Valid @ModelAttribute LoginDto loginDto,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws BizException {

        log.info("开始登录，loginDto:{}", loginDto);
        if (StrUtil.isBlank(loginDto.getDeviceName())) {
            loginDto.setDeviceName(userContext.currentTokenData().getDevice().name());
        }
        loginDto.setDeviceType(userContext.currentTokenData().getDevice());
        loginDto.setDeviceId(userContext.getDuid());
        loginDto.setGroupId(context.getGroupId());
        // 管理端登录验证码
        if (loginDto.getDeviceType() == DeviceType.admin) {
            String inputCodeMd5 = MD5Util.MD5Encode(loginDto.getCode() == null ? "" : loginDto.getCode().toLowerCase(Locale.ROOT));
//            if (!inputCodeMd5.equals(HttpWebUtil.getCookieValue(request, "image-code"))) {
//                throw new BizException(UserResultCode.smsCodeError);
//            }
        }
        return userSafe(() -> loginServiceComposite.login(loginDto, request, response), loginDto);
    }

    private Object userSafe(SupplierException<IUserModel<?>> supplier, LoginDto loginDto) throws BizException {
        UserModel userModel = null;
        //登录是否成功
        Integer isLoginOk = 1;
        BizException exception = null;
        try {
            userModel = (UserModel) supplier.get();
        } catch (BizException e) {
            if (e.getCode() == UserResultCode.loginAuthFail.getCode()) {
                isLoginOk = 0;
                exception = e;
            } else {
                throw e;
            }
        }
//        ResultModel resultModel = UserSafeFeignRequestUtil.userLoginRecord(LoginRecordDto.builder().userName(loginDto.getUserAccount()).isPassWordError(isLoginOk == 1 ? 0 : 1).build());
//        Result<ResultModel> result = new Result<>();
//        if (isLoginOk.equals(0) && resultModel != null && resultModel.getLockStatus() == 0) {
//            result.setCode(UserResultCode.loginAuthFail.getCode());
//            result.setMessage(String.format(UserResultCode.loginAuthFailFormat.getMessage(), resultModel.getMaxCount(), resultModel.getMaxCount() - resultModel.getErrorCount()));
//            result.setData(resultModel);
//            return result;
//        } else if (resultModel != null && resultModel.getLockStatus() == 1) {
//            result.setCode(UserResultCode.loginInfoIsLockFormat.getCode());
//            result.setMessage(String.format(UserResultCode.loginInfoIsLockFormat.getMessage(), BigDecimal.valueOf(resultModel.getLockTime()).divide(BigDecimal.valueOf(60), RoundingMode.UP).intValue()));
//            result.setData(resultModel);
//            return result;
//        }
        if (null != exception) {
            throw exception;
        }
        redisTemplate.opsForValue().set(TokenFilter.userTokeKey(userModel.getId(), loginDto.getDeviceName()), userModel.getToken());
        return userModel;
    }

    @Operation(summary = "前端用户手机号密码注册接口")
    @Parameters({
            @Parameter(name = "deviceType", description = "设备类型", required = true),
            @Parameter(name = "deviceId", description = "设备id 移动端时必传"),
            @Parameter(name = "userAccount", description = "手机号", required = true),
            @Parameter(name = "code", description = "验证码", required = true),
            @Parameter(name = "credential", description = "密码", required = true)
    })
    @PostMapping("/register")
    @ApiTimeRecord(gt = 5000)
    @RequireContext(value = {com.caishi.lkx.common.context.Role.APP, com.caishi.lkx.common.context.Role.PC})
    public IUserModel<?> register(@Valid @ModelAttribute LoginDto loginDto, boolean haveLogin, HttpServletRequest request, HttpServletResponse response) throws BizException {
        loginDto.setUserType(UserType.member);
        loginDto.setGroupId(context.getGroupId());
        if (!smsUtilService.checkCode(loginDto.getUserAccount(), loginDto.getCode(), SmsUtilService.SmsCodeType.register)) {
            throw new BizException(UserResultCode.smsCodeError);
        }
        var user = TypeServiceRouter.router(loginDto.getUserType()).register(loginDto, request, response);
        //memberService.sendVers6(user.getId(), user.getHeader(), true);
        if (haveLogin) {
            LoginInfoModel loginInfoModel = loginInfoService.selectUserLoginInfo(user.getId(), loginDto.getType());
            return loginServiceComposite.success(loginInfoModel, loginDto, true, response);
        }
        return user;
    }
//    @PostMapping("/distribute/register")
//    @Roles({})
//    public void distributeRegister() throws BizException {
////        查看有无代理商，必须挂靠在代理商下
////        获取当前用户是否在分销系统用户表中，如果在表中，只能代理商与一级分销员有权限添加
//        String currentUserId = userContext.currentUserId();
//        DistributionUser user = IDistributionUserService.getById(currentUserId);
//        if(user!=null){
//            if(user.getLevelsId()>=2){
////                没有权限，这里是后端校验，前端也要校验一下减少请求，安全一点，虽然系统安全性并不是很高
//                throw new BizException(UserResultCode.getJiGuangUpdateUserInfoFail);
//            }else {
//
//            }
//        }else{
//
//        }
//        throw new BizException(UserResultCode.getJiGuangUpdateUserInfoFail);

    /// /        如果不在则是自主注册或他人推荐注册
//    }
    @Operation(summary = "忘记密码")
    @Parameters({
            @Parameter(name = "userAccount", description = "手机号", required = true),
            @Parameter(name = "code", description = "验证码", required = true),
            @Parameter(name = "credential", description = "密码", required = true)
    })
    @PostMapping("/forget-pass")
    public void forgetPassword(
            @Valid @ModelAttribute LoginDto loginDto
    ) throws BizException {
        if (!smsUtilService.checkCode(loginDto.getUserAccount(), loginDto.getCode(), SmsUtilService.SmsCodeType.forgetPass)) {
            throw new BizException(UserResultCode.smsCodeError);
        }
        String userId = userService.getUserIdByMobile(loginDto.getUserAccount());
        if (StrUtil.isBlank(userId)) {
            throw new BizException(UserResultCode.loginInfoNotFound);
        }
        loginInfoService.updateUserUsernameAndPasswordLoginInfo(userId, loginDto.getCredential(), null);
    }

    @Operation(summary = "重置密码")
    @Parameters({
            @Parameter(name = "userAccount", description = "手机号", required = true),
            @Parameter(name = "code", description = "验证码", required = true),
            @Parameter(name = "credential", description = "密码", required = true)
    })
    @PostMapping("/change-pass")
    @Roles(Role.onlyLogin)
    public void changePassword(
            @Valid @ModelAttribute LoginDto loginDto
    ) throws BizException {
        if (!smsUtilService.checkCode(loginDto.getUserAccount(), loginDto.getCode(), SmsUtilService.SmsCodeType.resetPass)) {
            throw new BizException(UserResultCode.smsCodeError);
        }
        String userId = userService.getUserIdByMobile(loginDto.getUserAccount());
        if (!userContext.currentUserId().equals(userId)) {
            throw new BizException("手机号错误");
        }
        loginInfoService.updateUserUsernameAndPasswordLoginInfo(userId, loginDto.getCredential(), null);
    }


    /// 退出登录并没有删除token
    @Operation(summary = "退出登录")
    @GetMapping("/logout")

    @Roles(Role.onlyLogin)
    public void logout(HttpServletRequest request, HttpServletResponse response, HttpServletResponse res) {
        var device = userContext.currentTokenData().getDevice();
        tokenFilter.responseRemoveToken(device, response);
        userDeviceService.settingUserDevice(userContext.currentUserId(), false, device, null);
    }

    @Operation(summary = "微信小程序手机号绑定")
    @Parameters({
            @Parameter(name = "encryptedData", description = "加密数据"),
            @Parameter(name = "iv", description = "向量"),
    })
    @PostMapping("wechat-bind-phone")
    public IUserModel<?> weChatBindPhone(@ModelAttribute LoginDto loginDto, String encryptedData, String iv, HttpServletResponse response) throws BizException {
        loginDto.setDeviceType(userContext.currentTokenData().getDevice());
        loginDto.setDeviceId(userContext.getDuid());
        String userId = userContext.currentUserId();
        UserModel userModel = userService.getById(userId);
        if (null != userModel && StrUtil.isNotBlank(userModel.getMobile())) {
            throw new BizException(UserResultCode.userHaveBindMobile);
        }
        String sessionKey = SpringContextHolder.getBean(WxcxLoginServiceImpl.class).getSessionKey(loginDto.getDeviceId());
        JSONObject decrypt = WeChatUtil.decrypt(sessionKey, encryptedData, iv);
        if (null == decrypt) {
            throw new BizException("解码错误");
        }
        String mobile = decrypt.get("purePhoneNumber").toString();
        if (StrUtil.isBlank(mobile)) {
            throw new BizException("获取手机号失败");
        }

        loginDto.setBindPhone(mobile);
        return loginServiceComposite.thirdBindPhone(loginDto, false, response);
    }

    @Operation(summary = "扫码登录")
    @Parameters({
            @Parameter(name = "code", description = "code码"),
            @Parameter(name = "mobile", description = "手机号码"),
    })
    @PostMapping("scan-login")
    @Roles
    public Boolean scanLogin(@RequestParam String code, @RequestParam String userAccount) throws BizException {
        if (Boolean.TRUE.equals(redisTemplate.hasKey(code))) {
            MemberModel memberModel = memberService.fetchVersRegister(userAccount);
            redisTemplate.opsForValue().set(code, memberModel.getId());
            return true;
        }
        return false;

    }

    @PostMapping("/third-bind-phone")
    @Operation(summary = "第三方登录绑定手机号")
    @Parameters({
            @Parameter(name = "deviceType", description = "设备类型", required = true),
            @Parameter(name = "deviceId", description = "设备id 移动端时必传"),
            @Parameter(name = "userAccount", description = "绑定手机号", required = true),
            @Parameter(name = "credential", description = "非快捷登录时必传密码"),
            @Parameter(name = "smsCodeType", description = "登录类型", required = true),
            @Parameter(name = "code", description = "验证码"),
    })
    public IUserModel<?> thirdBindPhone(@ModelAttribute LoginDto loginDto, HttpServletResponse response) throws BizException {
        return loginServiceComposite.thirdBindPhone(loginDto, true, response);
    }


    @DeleteMapping("/remove-third-bind")
    @Operation(summary = "移除第三方绑定")
    @Parameter(name = "type", description = "第三方绑定类型")
    @Roles(Role.onlyLogin)
    public Boolean removeThirdBind(@RequestParam UserAccountType type) {
        return loginServiceComposite.removeThirdBind(userContext.currentUserId(), type);
    }

    @PostMapping("/syncky")
    @Operation(summary = "同步vers6数据")
    @Roles
    @Transactional
    public Boolean syncKy(@RequestParam String code) throws InterruptedException {
        OssUserModel userModel = memberService.decryptionVers(code);
        if (StrUtil.isBlank(userModel.getUserName())) {
            return false;
        }
        redissonLock.tryLock("syncky:userName:" + userModel.getUserName(), key -> {
            String userId = memberService.getUserIdByssoUserModel(userModel);
            try {
                memberService.saveVers6(userModel, userId);
            } catch (Exception e) {
                log.error("保存vers用户错误", e);
            }
        });

        return true;
    }

    @GetMapping("/carsi/authorize")
    @Roles
    public void carsiAuthorize(@RequestParam String entityid, HttpServletResponse response) throws IOException {
        String carsiWAYFlessUrl = thirdOauthConfig.getCarsiWAYFlessUrl();
        String target = "https://spoauth2.carsi.edu.cn/api/authorize?response_type=code&client_id=" + thirdOauthConfig.getCarsiClientId() + "&state=" + thirdOauthConfig.getCarsiClientId();
        String format = String.format(carsiWAYFlessUrl, entityid, URLEncoder.encode(target, "UTF-8"));
        response.sendRedirect(format);
    }

    @GetMapping("/sso")
    @Roles
    public void sso(@CookieValue(name = "ossUserInfo", required = false) String userInfo, @RequestParam(required = false) Integer type, HttpServletRequest request, HttpServletResponse response) throws IOException, BizException {
        try {
            if (!userContext.loginStatus() && StrUtil.isNotBlank(userInfo)) {

                OssUserModel userModel = memberService.decryptionVers(userInfo);
                //解密
                LoginInfoModel loginInfoModel = loginInfoService.selectLoginInfoUserAccount(userModel.getUserName(), UserAccountType.username);
                if (null == loginInfoModel) {
                    UserModel userByMobile = memberService.getUserByMobile(userModel.getMobile());
                    if (null != userByMobile) {
                        List<LoginInfoModel> loginInfoModels = loginInfoService.selectUserLoginInfo(userByMobile.getId());
                        if (!loginInfoModels.isEmpty()) {
                            MemberModel memberModel = memberService.getById(userByMobile.getId());
                            if (null == memberModel.getGroupId() && StrUtil.isNotBlank(userModel.getGroupUserId())) {
                                if (null != userModel.getKaoyanExpireTime()) {
                                    memberModel.setKaoyanExpireTime(userModel.getKaoyanExpireTime().atTime(23, 59, 59));
                                }
                                if (null != userModel.getExpireTime()) {
                                    memberModel.setExpireTime(userModel.getExpireTime().atTime(23, 59, 59));
                                }
                                memberService.bindGroupCode(memberModel, userModel.getGroupUserId());

                            }
                            loginInfoModel = loginInfoModels.get(0);
                        }
                    }
                }
                LoginDto loginDto = new LoginDto();
                loginDto.setDeviceName(DeviceType.pc.name());
                loginDto.setDeviceType(DeviceType.pc);
                loginDto.setDeviceId(userContext.getDuid());
                loginDto.setUserTypes(new UserType[]{UserType.member});
                if (null == loginInfoModel) {
                    //不存在用户创建用户
                    MemberModel memberModel = memberService.saveVers6(userModel, null);
                    loginInfoModel = loginInfoService.selectUserLoginInfo(memberModel.getId(), UserAccountType.username);
                }
                loginServiceComposite.success(loginInfoModel, loginDto, true, response);
            }
        } catch (Exception e) {
            log.error("单点登录失败", e);
        } finally {
            //重定向到首页
            String s;
            if (null != type && type == 2) {
                s = request.getScheme() + "://" + "kaoyan.cqvip.com";
            } else {
                s = request.getScheme() + "://" + "vers.cqvip.com";
            }
            response.sendRedirect(s);
        }


    }

    @GetMapping("/sso-rspx")
    @Roles
    public String ssoRspx(@CookieValue(name = "ossUserInfo", required = false) String userInfo, @RequestParam(required = false) Integer type, HttpServletRequest request, HttpServletResponse response) throws IOException, BizException {
        log.info("获取info,{}", userInfo);
        try {
            if (!userContext.loginStatus() && StrUtil.isNotBlank(userInfo)) {

                OssUserModel userModel = memberService.decryptionCqrspx(userInfo);
                //解密
                LoginInfoModel loginInfoModel = loginInfoService.selectLoginInfoUserAccount(userModel.getUserName(), UserAccountType.orgLogin);

                LoginDto loginDto = new LoginDto();
                loginDto.setDeviceName(DeviceType.cqrspxpc.name());
                loginDto.setDeviceType(DeviceType.cqrspxpc);
                loginDto.setDeviceId(userContext.getDuid());
                loginDto.setUserTypes(new UserType[]{UserType.member});
                //设置平台id写死为重庆人社培训网id
                loginDto.setGroupId(13L);
                userModel.setGroupId(13L);
                if (null == loginInfoModel) {
                    //不存在用户创建用户
                    MemberModel memberModel = memberService.saveCqrspx(userModel, null);
                    loginInfoModel = loginInfoService.selectUserLoginInfo(memberModel.getId(), UserAccountType.orgLogin);
                }
                loginServiceComposite.success(loginInfoModel, loginDto, true, response);
            }
        } catch (Exception e) {
            log.error("重庆人社培训网单点登录失败", e);
            return "FAIL";
        } finally {
            //重定向到首页
            var s = request.getScheme() + "://" + "trial.cqrspx.cn";
            response.sendRedirect(s);
        }
        return "SUCCESS";

    }

    @GetMapping("/heart")
    public void heart() throws Exception {
        String userId = userContext.currentUserId();
        if (userId == null) {
            return;
        }
        String s = userContext.currentIp();
//        插入记录
        String locationWithIp = userActiveService.getLocationWithIp(s);
//        String locationWithIp = "111";

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0);
        LocalDateTime end = start.plusDays(1L);
        QueryWrapper<UserActive> queryWrapper = new QueryWrapper<UserActive>().eq("user_id", userId).between("start_time", start, end).orderByDesc("start_time");

        List<UserActive> list = userActiveService.list(queryWrapper);
        if (list.isEmpty()) {
            UserActive userActive = new UserActive();
            userActive.setUserId(userId);
            userActive.setLocation(locationWithIp);
            userActive.setStartTime(now);
            userActive.setEndTime(now);
            userActiveService.save(userActive);
        } else {
//            如果endTime为null，即超时，重新生成一条记录
//                如果endTime不为null，即未超时，更新endTime
            UserActive userActive = list.get(0);

            long between = ChronoUnit.MINUTES.between(userActive.getEndTime(), now);
            if (between > 60) {
                userActive.setId(null);
                userActive.setStartTime(now);
                userActive.setEndTime(now);
                userActiveService.save(userActive);
            } else {
                userActive.setEndTime(now);
                userActiveService.updateById(userActive);
            }
        }
    }

//    统计用户,某个区间范围内的活跃用户
    @GetMapping("/active-count")
    public com.alibaba.fastjson2.JSONObject activeCount(@RequestParam String startTime, @RequestParam String endTime) {
        LocalDateTime start = LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime end = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        QueryWrapper<UserActive> queryWrapper = new QueryWrapper<UserActive>().between("start_time", start, end).select("distinct user_id");
//        startTime
//        查询总用户
        QueryWrapper<UserModel> userWrapper = new QueryWrapper<UserModel>().eq("deleted", 0).eq("user_type",2).ne("user_status",2);
        Long l = userService.getMapper().selectCount(userWrapper);
        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
        jsonObject.put("total", l);
        long count = userActiveService.count(queryWrapper);
        jsonObject.put("active", count);
        return jsonObject;
    }
}
